feat(blog): theme cover-fallback gradients by library#941
Conversation
Blog posts without a header image now generate a gradient palette around their library's primary hue (Virtual → purple, Query → red, etc.) instead of picking one of eight slug-hashed palettes. Posts without a `library` frontmatter keep the existing slug-hash behavior. Also reworked the blob generator so gradients read as soft watercolor washes: bigger radii with ry biased > rx to compensate for wide containers, anchors scattered across the canvas with per-slug jitter, and soft falloff stops so blobs blend into each other rather than rendering as discrete circles. A linear base tint sits underneath so edges never wash out to the wrapper background.
📝 WalkthroughWalkthroughThis PR extends blog cover fallback styling with library-aware gradients. The gradient system is refactored to support color palettes derived from library metadata, while the blog post data layer and component tree are extended to flow the library information through to the styling layer. ChangesLibrary-aware blog cover gradients
Sequence Diagram(s)sequenceDiagram
participant BlogPost as BlogPost<br/>Component
participant CoverFallback as CoverFallback<br/>Component
participant gradientBg as gradientBackgroundCss<br/>Function
BlogPost->>CoverFallback: render with slug, library
CoverFallback->>gradientBg: call(slug, library)
Note over gradientBg: paletteFor checks LIBRARY_HUES<br/>for library primary hue
Note over gradientBg: blobsFor generates ellipse<br/>blobs with deterministic jitter
Note over gradientBg: blobsToCss creates radial<br/>gradients + tint layer
gradientBg-->>CoverFallback: CSS string
CoverFallback->>CoverFallback: apply as backgroundImage style
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/utils/ogGradient.ts (1)
139-142: ⚡ Quick winNormalize library IDs before hue lookup.
This lookup is case-sensitive and also fails when the first CSV token is empty, causing silent fallback to slug palettes. Normalizing + selecting first non-empty token makes theming more robust.
Proposed patch
- if (library) { - const firstId = library.split(',')[0]?.trim() + if (library) { + const firstId = library + .split(',') + .map((id) => id.trim().toLowerCase()) + .find(Boolean) const baseHue = firstId ? LIBRARY_HUES[firstId] : undefined if (baseHue !== undefined) { return paletteFromHue(baseHue) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/utils/ogGradient.ts` around lines 139 - 142, When resolving the palette hue from the CSV "library" string, normalize and skip empty tokens: split library by ',', map each token to token.trim().toLowerCase() (or otherwise match the casing used in LIBRARY_HUES), filter out empty strings, then take the first non-empty token as firstId before looking up LIBRARY_HUES[firstId]; update the existing logic around the variables library, firstId and baseHue so the lookup is case-insensitive and does not pick an empty token (preserving the existing fallback to slug palettes when no valid firstId is found).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/utils/ogGradient.ts`:
- Around line 139-142: When resolving the palette hue from the CSV "library"
string, normalize and skip empty tokens: split library by ',', map each token to
token.trim().toLowerCase() (or otherwise match the casing used in LIBRARY_HUES),
filter out empty strings, then take the first non-empty token as firstId before
looking up LIBRARY_HUES[firstId]; update the existing logic around the variables
library, firstId and baseHue so the lookup is case-insensitive and does not pick
an empty token (preserving the existing fallback to slug palettes when no valid
firstId is found).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8da1a73a-56e4-4521-86db-24eb9461e9a1
📒 Files selected for processing (5)
src/components/BlogCard.tsxsrc/components/CoverFallback.tsxsrc/routes/blog.$.tsxsrc/utils/blog.functions.tssrc/utils/ogGradient.ts
Summary
Blog posts without an explicit
headerImagenow generate their cover-fallback gradient palette around the library's primary hue fromlibraries.ts(Virtual → purple, Query → red, AI → pink, etc.) so the placeholder feels branded instead of random. Posts without alibraryfrontmatter keep the existing slug-hashed palette.Also reworked the blob generator so the result reads as a soft watercolor wash rather than a single centered radial or polka-dot accents:
ry ≈ 1.5–2× rxto compensate for wide 5:2 / 16:9 containers so blobs don't squish into horizontal bands.Test plan
Summary by CodeRabbit